#include "ct.client.h"

#include <cstring>

TOB::CT::Client::Client(TOB::Nameserver::IP nsip, std::string username, std::string password) : AuthRequired(nsip, username, password) {
  Nameserver::IP ip;
  std::string addr;
  rpcConnected = false;

  Nameserver::convertIP(nsip, addr);
  Nameserver ns(addr);
  if(ns.requestAddress(Server::UserStorage, ip) != Code::Okay) { return; }
  Nameserver::convertIP(ip, addr);
  
  clnt = clnt_create(addr.c_str(), CT_PROG, CT_VERSION, "udp");
  if(clnt == nullptr) { return; }
  rpcConnected = true;
}

TOB::CT::Client::~Client() {
  if (rpcConnected && clnt != nullptr) {
    clnt_destroy(clnt);
  }
}

Code::Type TOB::CT::Client::connect() {
  int* respond;
  Userdata user;
  
  int length = username.length();
  memcpy(user.username, username.c_str(), (length > 32) ? 32 : length);
  user.username[32] = 0;
  
  length = password.length();
  memcpy(user.password, password.c_str(), (length > 32) ? 32 : length);
  user.password[32] = 0;
  
  user.nsip = static_cast<int>(nsip);
  
  respond = connect_1(&user, clnt);
  if (respond == NULL || static_cast<Code::Type>(*respond) != Code::Okay) {
    //exception
    return static_cast<Code::Type>(*respond);
  }
  return Code::Okay;
}

Code::Type TOB::CT::Client::disconnect() {
  int* respond;
  Userdata user;
  
  int length = username.length();
  memcpy(user.username, username.c_str(), (length > 32) ? 32 : length);
  user.username[32] = 0;
  
  length = password.length();
  memcpy(user.password, password.c_str(), (length > 32) ? 32 : length);
  user.password[32] = 0;
  
  user.nsip = static_cast<int>(nsip);
  
  respond = disconnect_1(&user, clnt);
  if (respond == NULL || static_cast<Code::Type>(*respond) != Code::Okay) {
    //exceptions
    return static_cast<Code::Type>(*respond);
  }
  return Code::Okay;
}

Code::Type TOB::CT::Client::getEvent(ChatEvent::Type& event) {
  int* respond;
  if (!rpcConnected || clnt != NULL) {
    //exception
    event = ChatEvent::ServerError;
    return Code::CT_DoesntRespond;
  }
  respond = getevent_1(NULL, clnt);
  if (respond == NULL) {
    //exception
    return Code::CT_FunctionCallFailure;
  }
  event = static_cast<ChatEvent::Type>(*respond);
  return Code::Okay;
}

Code::Type TOB::CT::Client::getMsg(Message& msg) {
  GetMessage* gmsg;
  if (!rpcConnected || clnt != NULL) {
    //exception
    return Code::CT_DoesntRespond;
  }
  gmsg = getmsg_1(NULL, clnt);
  if (gmsg == NULL) {
    //exception
    return Code::CT_FunctionCallFailure;
  }
  msg.data = gmsg->data;
  msg.user = gmsg->user;
  msg.type = static_cast<MessageType::Type>(gmsg->type);
  msg.time = static_cast<time_t>(gmsg->time);
  return Code::Okay;
}

Code::Type TOB::CT::Client::updateUsers() {
  int* respond;
  if (!rpcConnected || clnt != NULL) {
    //exception
    return Code::CT_DoesntRespond;
  }
  respond = startgetusers_1(NULL, clnt);
  if (respond == NULL) {
    //exeption
    return Code::CT_FunctionCallFailure;
  }
  
  users.clear();
  RPCUser* rpcuser = NULL;
  do {
    rpcuser = getuser_1(respond, clnt);
    if (rpcuser == NULL) {
      //exception
      return Code::CT_FunctionCallFailure;
    }
    User user;
    user.name = rpcuser->name;
    user.status = rpcuser->status;
    user.statusmsg = rpcuser->statusmsg;
    users.push_back(user);
    if (rpcuser->count == 0) {
      break;
    }
  } while (true);
  
  respond = endgetusers_1(respond, clnt);
  if (respond == NULL) {
    //exeption
    return Code::CT_FunctionCallFailure;
  }
  return Code::Okay;
}

Code::Type TOB::CT::Client::sendMessage(std::string& msg) {
  int* respond;
  if (!rpcConnected || clnt != NULL) {
    //exception
    return Code::CT_DoesntRespond;
  }
  
  SendMessage sendmsg;
  
  int length = username.length();
  memcpy(sendmsg.user.username, username.c_str(), (length > 32) ? 32 : length);
  sendmsg.user.username[32] = 0;
  
  length = password.length();
  memcpy(sendmsg.user.password, password.c_str(), (length > 32) ? 32 : length);
  sendmsg.user.password[32] = 0;
  
  sendmsg.user.nsip = static_cast<int>(nsip);
  
  length = msg.length();
  memcpy(sendmsg.data, msg.c_str(), (length > 4096) ? 4096 : length);
  sendmsg.data[4096] = 0;
  
  sendmsg.type = static_cast<int>(MessageType::incoming);
  
  respond = sendmsg_1(&sendmsg, clnt);
  if (respond == NULL) {
    //exeption
    return Code::CT_FunctionCallFailure;
  }
  
  return Code::Okay;
}
